#pragma once
#include <iostream>
#include <queue>

// namespace BlockQueueModule
// {
// version 1 -> 利用系统调用实现生产者消费者模型
// static const int gcap = 10;
// template <typename T>
// class BlockQueue
// {
// private:
//     bool IsFull() { return _q.size() == _cap; }
//     bool IsEmpty() { return _q.empty(); }

// public:
//     BlockQueue(int cap = gcap)
//         : _cap(cap), _cwait_num(0), _pwait_num(0)
//     {
//         pthread_mutex_init(&_mutex, nullptr);
//         pthread_cond_init(&_productor_cond, nullptr);
//         pthread_cond_init(&_consumer_cond, nullptr);
//     }

//     void Equeue(const T &in)    // 生产者
//     {
//         pthread_mutex_lock(&_mutex);
//         if (IsFull())
//         {
//             _pwait_num++; // 若空间满了，就让等待的生产者数量++
//             pthread_cond_wait(&_productor_cond, &_mutex);
//             _pwait_num--;
//         }
//         _q.push(in);
//         // 肯定有数据，唤醒消费者
//         if (_cwait_num)
//         {
//             pthread_cond_signal(&_consumer_cond);
//         }
//         pthread_mutex_unlock(&_mutex);
//     }
//     void Pop(T *out)    // 消费者
//     {
//         pthread_mutex_lock(&_mutex);
//         if (IsEmpty())
//         {
//             _cwait_num++; // 若数据空了，就让等待的消费者数量++
//             pthread_cond_wait(&_consumer_cond, &_mutex);
//             _cwait_num--;
//         }
//         *out = _q.front();
//         _q.pop();
//         // 肯定有空间，唤醒生产者
//         if (_pwait_num)
//         {
//             pthread_cond_signal(&_productor_cond);
//         }
//         pthread_mutex_unlock(&_mutex);
//     }
//     ~BlockQueue()
//     {
//         pthread_mutex_destroy(&_mutex);
//         pthread_cond_destroy(&_productor_cond);
//         pthread_cond_destroy(&_consumer_cond);
//     }
// private:
//     std::queue<T> _q;               // 保存数据的容器，临界资源
//     int _cap;                       // bq最大容量
//     pthread_mutex_t _mutex;         // 互斥
//     pthread_cond_t _productor_cond; // 生产者条件变量
//     pthread_cond_t _consumer_cond;  // 消费者条件变量

//     int _cwait_num; // 在条件变量下等待的消费者数量
//     int _pwait_num; // 在条件变量下等待的生产者数量
//     };
// }

// version 2 ->利用自己封装的锁和条件变量实现生产者消费者模型
#include "Mutex.hpp"
#include "Cond.hpp"
namespace BlockQueueModule
{
    using namespace CondModule;
    using namespace LockModule;

    static const int gcap = 10;
    template <typename T>
    class BlockQueue
    {
    private:
        bool IsFull() { return _q.size() == _cap; }
        bool IsEmpty() { return _q.empty(); }

    public:
        BlockQueue(int cap = gcap)
            : _cap(cap), _cwait_num(0), _pwait_num(0)
        {}
        void Equeue(const T &in) // 生产者
        {
            // 自己封装的RAII风格的锁
            LockGuard lock(_mutex);
            if (IsFull())
            {
                _pwait_num++; // 若空间满了，就让等待的生产者数量++
                _productor_cond.Wait(_mutex);
                _pwait_num--;
            }
            _q.push(in);
            // 肯定有数据，唤醒消费者
            if (_cwait_num)
            {
                _consumer_cond.Notify();
            }
        }
        void Pop(T *out) // 消费者
        {
            LockGuard lock(_mutex);
            if (IsEmpty())
            {
                _cwait_num++; // 若数据空了，就让等待的消费者数量++
                _consumer_cond.Wait(_mutex);
                _cwait_num--;
            }
            *out = _q.front();
            _q.pop();
            // 肯定有空间，唤醒生产者
            if (_pwait_num)
            {
                _productor_cond.Notify();
            }
        }
        ~BlockQueue()
        {}
    private:
        std::queue<T> _q;               // 保存数据的容器，临界资源
        int _cap;                       // bq最大容量
        Mutex _mutex;         // 互斥
        Cond _productor_cond; // 生产者条件变量
        Cond _consumer_cond;  // 消费者条件变量

        int _cwait_num; // 在条件变量下等待的消费者数量
        int _pwait_num; // 在条件变量下等待的生产者数量
    };
}